package com.xhj.mybatis.type;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * BaseTypeHandler
 *
 * @author XJks
 * @description BaseTypeHandler 是一个抽象基类，用于处理 MyBatis 中 Java 类型与 JDBC 类型之间的转换。该类为泛型，T 表示要处理的 Java 类型。
 */
public abstract class BaseTypeHandler<T> implements TypeHandler<T> {
    /**
     * 设置 PreparedStatement 的参数。
     * 如果参数为 null，必须指定 jdbcType，否则抛出异常。
     * 如果参数不为 null，调用子类实现的 setNonNullParameter 方法。
     *
     * @param ps        PreparedStatement 对象
     * @param i         参数索引（从 1 开始）
     * @param parameter 参数值
     * @param jdbcType  JDBC 类型
     * @throws SQLException SQL 异常
     */
    @Override
    public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
        // 如果参数为 null
        if (parameter == null) {
            // JDBC 类型也为 null，抛出异常
            if (jdbcType == null) {
                throw new RuntimeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
            }
            try {
                // 设置 SQL 参数为 null，并指定 JDBC 类型
                ps.setNull(i, jdbcType.TYPE_CODE);
            } catch (SQLException e) {
                // 设置失败，抛出自定义异常并附带详细信息
                throw new RuntimeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . "
                        + "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. "
                        + "Cause: " + e, e);
            }
        } else {
            try {
                // 参数不为 null，调用子类实现的 setNonNullParameter 方法
                setNonNullParameter(ps, i, parameter, jdbcType);
            } catch (Exception e) {
                // 设置失败，抛出自定义异常并附带详细信息
                throw new RuntimeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . "
                        + "Try setting a different JdbcType for this parameter or a different configuration property. "
                        + "Cause: " + e, e);
            }
        }
    }

    /**
     * 从结果集中通过列名获取结果的方法。
     *
     * @param rs         查询结果集（ResultSet）
     * @param columnName 列名（当 useColumnLabel=false 时使用列名）
     * @return 返回转换后的 Java 类型结果
     * @throws SQLException 如果获取结果时发生 SQL 异常
     */
    @Override
    public T getResult(ResultSet rs, String columnName) throws SQLException {
        try {
            return getNullableResult(rs, columnName);
        } catch (Exception e) {
            // 获取失败，抛出自定义异常并附带详细信息
            throw new RuntimeException("Error attempting to get column '" + columnName + "' from result set.  Cause: " + e, e);
        }
    }

    /**
     * 从 ResultSet 结果集中通过列索引获取结果。
     * 实际获取逻辑由子类实现的 getNullableResult 完成。
     *
     * @param rs          结果集对象
     * @param columnIndex 列索引（从 1 开始）
     * @return 查询结果
     * @throws SQLException SQL 异常
     */
    @Override
    public T getResult(ResultSet rs, int columnIndex) throws SQLException {
        try {
            return getNullableResult(rs, columnIndex);
        } catch (Exception e) {
            // 获取失败，抛出自定义异常并附带详细信息
            throw new RuntimeException("Error attempting to get column #" + columnIndex + " from result set.  Cause: " + e, e);
        }
    }

    /**
     * 抽象方法：设置非 null 参数到 PreparedStatement。
     * 具体实现由子类完成。
     *
     * @param ps        PreparedStatement 对象
     * @param i         参数索引
     * @param parameter 参数值（非 null）
     * @param jdbcType  JDBC 类型
     * @throws SQLException SQL 异常
     */
    public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

    /**
     * 抽象方法：通过列名从 ResultSet 获取可为 null 的结果。
     * 具体实现由子类完成。
     *
     * @param rs         结果集对象
     * @param columnName 列名
     * @return 查询结果
     * @throws SQLException SQL 异常
     */
    public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;

    /**
     * 抽象方法：通过列索引从 ResultSet 获取可为 null 的结果。
     * 具体实现由子类完成。
     *
     * @param rs          结果集对象
     * @param columnIndex 列索引
     * @return 查询结果
     * @throws SQLException SQL 异常
     */
    public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException;
}
